package com.cmbc.afw.demo.merchant.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.cmbc.afw.interceptor.IChainInvoker;
import com.cmbc.afw.interceptor.IInterceptor;
import com.cmbc.afw.security.model.SecurityResult;
import com.cmbc.afw.security.utils.SecurityUtil;
import com.cmbc.afw.util.tx.message.RequestHeader;
import com.cmbc.afw.util.tx.message.RequestMsg;
import com.cmbc.afw.util.tx.message.Response;
import com.cmbc.afw.util.tx.message.ResponseHeader;
import com.cmbc.afw.utils.exception.EStatusException;
import com.cmbc.afw.utils.exception.RStatusException;
import com.cmbc.afw.utils.httpclient.ClientConfig;
import com.cmbc.afw.utils.parameter.DBParameterService;
import com.cmbc.afw.utils.util.TxContext;

/**
 *	入访商户安全处理拦截器
 *	商户接收外联平台响应时的安全处理
 */
public class DefaultInSecurityInterceptor implements IInterceptor<Object,Object> {

    private static final Logger logger = LoggerFactory.getLogger(DefaultInSecurityInterceptor.class);

    private ClientConfig clientConfig;

    public Object doFilter(Object input,
                           IChainInvoker<Object, Object> chainInvoker) {
        RequestHeader header = TxContext.getInboundReqHeader();
        validateHeaderForSecu(header);

        RequestMsg requestMsg = (RequestMsg)TxContext.getInboundReqData();
        setClientConfig();
        doInboundRequestSecurityProcess(header , requestMsg);

        Object output = chainInvoker.doInvoke(input);

        Object response = TxContext.getInboundRespData();

        if(response instanceof Response){
            doInboundResponseSecurityProcess((Response) response);
        } else {
            //这种情况无法从Response中获取merchantNum，继续从RequestHeader中取。
            String strBody = response.toString();
            doInboundResponseSecurityProcess(strBody);
        }

        return output;
    }

    /**
     * 进行入访响应安全处理
     * @param strBody
     */
    private void doInboundResponseSecurityProcess(String strBody) {
        if (!clientConfig.getSecuritySwitch()) {
            return;
        }

        if ( null == strBody) {
            return ;
        }

        //签名
        String sgntr = null;
        String errCode = null ;
        try {
            sgntr = SecurityUtil.sign(strBody, clientConfig.getOwnSm2Path(), clientConfig.getOwnSm2Pwd());
        } catch (Throwable t) {
            errCode = "XASFSECINB04";
            String errMsg = "入访响应报文签名操作失败";
            String msg = "错误代码:" + errCode + "，错误消息:" + errMsg;
            logger.error(msg);
            throw new RStatusException(errCode, errMsg, t);
        }

        //加密
        String cipherText = null;
        try {
            cipherText = SecurityUtil.encrypt(strBody, clientConfig.getOtherCertPath());
        } catch (Throwable t) {
            errCode = "XASFSECINB05";
            String errMsg = "入访响应报文加密操作失败";
            String msg = "错误代码:" + errCode + "，错误消息:" + errMsg;
            logger.error(msg);
            throw new RStatusException(errCode, errMsg, t);
        }

        //设置安全处理后的入访响应信息
        TxContext.setInboundRespData(cipherText);
        TxContext.setInboundRespSgnr(sgntr);

    }

    /**
     * 进行入访响应安全处理
     * @param response
     */
    private void doInboundResponseSecurityProcess(Response response) {
        if (!clientConfig.getSecuritySwitch()) {
            return;
        }

        if (response.getBody() == null) {
            return ;
        }

        String strBody = response.getBody().toString();
        ResponseHeader respHeader = response.getHeader();

        //签名
        String sgntr = null;
        String errCode = null ;
        try {
            sgntr = SecurityUtil.sign(strBody, clientConfig.getOwnSm2Path(), clientConfig.getOwnSm2Pwd());
        } catch (Throwable t) {
            errCode = "XASFSECINB04";
            String errMsg = "入访响应报文签名操作失败";
            String msg = "错误代码:" + errCode + "，错误消息:" + errMsg;
            logger.error(msg);
            throw new RStatusException(errCode, errMsg, t);
        }

        //加密
        String cipherText = null;
        try {
            cipherText = SecurityUtil.encrypt(strBody, clientConfig.getOtherCertPath());
        } catch (Throwable t) {
            errCode = "XASFSECINB05";
            String errMsg = "入访响应报文加密操作失败";
            String msg = "错误代码:" + errCode + "，错误消息:" + errMsg;
            logger.error(msg);
            throw new RStatusException(errCode, errMsg, t);
        }

        //设置安全处理后的入访响应信息
        respHeader.setSgntr(sgntr);
        response.setBody(cipherText);
        TxContext.setInboundRespData(response);
        TxContext.setInboundRespSgnr(sgntr);

    }

    /**
     * 进行入访请求安全处理
     * @param header
     * @param requestMsg
     */
    private void doInboundRequestSecurityProcess(RequestHeader header,
                                                 RequestMsg requestMsg) {
        if (!clientConfig.getSecuritySwitch()) {
            return ;
        }

        if (requestMsg.getBody() == null) {
            return ;
        }

        //解密
        String bodyString = requestMsg.getBody().toString();
        SecurityResult securityResult = null;
        try{
            securityResult = SecurityUtil.decryptAndVerify(bodyString, header.getSgntr(), clientConfig.getOwnSm2Path(), clientConfig.getOwnSm2Pwd(), clientConfig.getOtherCertPath());
        } catch (Throwable t) {
            String errCode = "XASFSECINB01";
            String errMsg = "入访请求报文解密验签操作失败";
            String msg = "错误代码:" + errCode + "，错误消息:" + errMsg;
            logger.error(msg);
            throw new EStatusException(errCode, errMsg, t);
        }

        requestMsg.setBody(securityResult.getPlainText());
        header.setSgntr(securityResult.getSignature());
        TxContext.setInboundReqData(requestMsg);
    }

    /**
     * 验证通讯报文头中的字段
     *
     * @function
     * @param header
     */
    public void validateHeaderForSecu(RequestHeader header) {
        if (header == null) {
            String errCode = "XASFPRTCL001";
            String errMsg = "通讯报文缺少协议报文头";
            logger.error("错误代码：" + errCode + "。错误信息：" + errMsg);
            throw new EStatusException(errCode, errMsg);
        }

        String transcode = header.getTransCode();
        if (!StringUtils.hasText(transcode)) {
            String errCode = "XASFPRTCL002";
            String errMsg = "通讯报文头缺少交易码信息";
            logger.error("错误代码：" + errCode + "。错误信息：" + errMsg);
            throw new EStatusException(errCode, errMsg);
        }
    }

    /**
     * 设置安全相关参数
     * 可自定义这些参数的获取方式，如配置文件、数据库等实现
     */
    private void setClientConfig() {
        clientConfig = new ClientConfig();
        //TODO

        clientConfig.setOtherCertPath("D:/test/cert/cmbcTest.cer");
        clientConfig.setOwnSm2Path("D:/test/cert/5001.sm2");
        clientConfig.setOwnSm2Pwd("123123");
        clientConfig.setSecuritySwitch(Boolean.TRUE);
    }

}
